package com.duapk.search.work;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.function.CustomScoreQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.duapk.common.Result;
import com.duapk.common.SearchResult;
import com.duapk.nlp.bean.RewriteResult;
import com.duapk.nlp.util.StringUtils;
import com.duapk.search.bean.Highlighter;
import com.duapk.search.bean.IndexBrief;
import com.duapk.search.bean.ReturnField;
import com.duapk.search.bean.SearchRequest;
import com.duapk.search.conf.ConfigFactory;
import com.duapk.search.constant.Constant;

/**
 * 搜索代理类
 * 
 * @Package: com.duapk.search.work
 * @ClassName: SearchProxy
 * @author: jiangzheng
 * @date: Apr 4, 2011 7:45:37 PM
 * 
 */
public class SearchProxy {

	private static Logger logger = LoggerFactory.getLogger(SearchProxy.class);

	private static RewriterProxy proxy = RewriterProxy.getInstance();

	/**
	 * 返回字段信息
	 */
	private static Map<String, ReturnField> returnFields = new HashMap<String, ReturnField>();

	/**
	 * 搜索字段权重信息
	 */
	private static Map<String, Float> searchFields = new HashMap<String, Float>();

	private static FieldSelector selector = new ApkFieldSelector(returnFields);

	static {
		List<String> fieldNames = ConfigFactory
				.getList("Response.returnFields.returnField[@name]");
		int index = 0;
		for (String fieldName : fieldNames) {
			ReturnField returnField = new ReturnField();
			returnField.setName(fieldName);
			returnField.setHighlight(ConfigFactory
					.getBoolean("Response.returnFields.returnField(" + index
							+ ")[@highlight]"));
			returnFields.put(fieldName, returnField);
			index++;
		}

		List<String> searchFieldNames = ConfigFactory
				.getList("Searcher.fields.field[@name]");
		index = 0;
		for (String searchFieldName : searchFieldNames) {
			searchFields.put(searchFieldName, ConfigFactory
					.getFloat("Searcher.fields.field(" + index + ")[@boost]"));
		}
	}

	/**
	 * 进行索引搜索
	 * 
	 * @Title: search
	 * @author: jiangzheng
	 * @date: Apr 4, 2011 7:48:05 PM
	 * @param request
	 * @return
	 */
	public static SearchResult search(SearchRequest request) {
		SearchResult searchResult = query(request);
		if (logger.isDebugEnabled()) {
			logger.debug("request:\n{}\nresponse:{}\n", request, searchResult);
		}
		return searchResult;
	}

	/**
	 * 进行索引查询
	 * 
	 * @Title: doSearch
	 * @author: jiangzheng
	 * @date: Apr 4, 2011 9:51:31 PM
	 * @return
	 */
	private static SearchResult query(SearchRequest request) {
		String keyword = request.getKeyword().trim();

		// 进行查询重写
		Float boost = searchFields.get(request.getField());
		RewriteResult rewriteResult = proxy.rewrite(keyword,
				request.getField(), boost == null ? 0 : boost.floatValue());
		if (!rewriteResult.isSuccess()) {
			SearchResult searchResult = new SearchResult();
			searchResult.setResultList(new ArrayList<Result>());
			return searchResult;
		}
		Query query = createQuery(rewriteResult.getQuery(), keyword, request);

		// 进行索引查询
		SearchResult result = doSearch(query, new Highlighter(rewriteResult
				.getTokenSet()), request);
		return result;
	}

	/**
	 * 进行索引查询
	 * 
	 * @Title: doSearch
	 * @author: jiangzheng
	 * @date: Jun 7, 2011 2:15:33 PM
	 * @param query
	 *            查询语句
	 * @param highlighter
	 *            高亮器
	 * @param request
	 *            搜索请求对象
	 * @return
	 */
	private static SearchResult doSearch(Query query, Highlighter highlighter,
			SearchRequest request) {
		SearchResult searchResult = new SearchResult();
		String category = Constant.APK;
		if (request.getCategory() != null && !request.getCategory().isEmpty()
				&& !Constant.ALL.equals(request.getCategory())) {
			category = request.getCategory();
		}
		IndexSearcher searcher = IndexSearcherManager
				.getIndexSearcher(category);
		IndexBrief brief = IndexSearcherManager.getBrief(category);
		if (searcher == null) {
			return searchResult;
		}
		if (brief == null) {
			brief = new IndexBrief();
		}

		List<Result> results = new ArrayList<Result>();
		int curItems = 0;
		int pageSize = request.getPageSize();
		int pid = request.getPid();

		// 最少搜索100条记录
		int nDocs = pageSize * pid;
		if (nDocs < Constant.MIN_RET_COUNT) {
			nDocs = Constant.MIN_RET_COUNT;
		} else {
			nDocs *= 2;
		}
		int start = pageSize * (pid - 1);

		Map<Integer, Integer> nameIDMap = new HashMap<Integer, Integer>();
		CustomScoreQuery customScoreQuery = new ApkCustomScoreQuery(query,
				brief, 0.25f);
		try {
			TopDocs topDocs = searcher.search(customScoreQuery, nDocs);
			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			int total = topDocs.totalHits;

			int items = 0;
			for (int i = 0; i < scoreDocs.length; i++) {
				int docId = scoreDocs[i].doc;
				int nameID = brief.getNameID(docId);
				Document doc = searcher.doc(docId, selector);
				if (!nameIDMap.containsKey(nameID)) {
					if (nameID != 0) {
						nameIDMap.put(nameID, items);
					}
					items++;
					if (items <= start) {
						continue;
					}
					if (curItems >= pageSize) {
						continue;
					}
					
					results.add(createResult(doc, highlighter, docId,
							scoreDocs[i].score));
					curItems++;
				} else {
					if (i <= Constant.MIN_RET_COUNT) {
						total--;
					}
					if (items >= start) {
						int index = nameIDMap.get(nameID);
						if (index < results.size()) {
							Result result = results.get(index);
							result.addNext(createResult(doc, highlighter,
									docId, scoreDocs[i].score));
						}
					}
				}
			}

			searchResult.setCurrentItem(curItems);
			searchResult.setResultList(results);
			searchResult.setTotalItem(total);
		} catch (IOException e) {
			logger.error("search error.", e);
		}
		return searchResult;
	}

	/**
	 * 生成结果记录
	 * 
	 * @Title: createResult
	 * @author: jiangzheng
	 * @date: Jun 7, 2011 4:05:12 PM
	 * @param doc
	 * @param highlighter
	 * @param docId
	 * @param score
	 * @return
	 */
	private static Result createResult(Document doc, Highlighter highlighter,
			int docId, float score) {
		Result result = new Result();
		String title = doc.get(Constant.NAME);
		result.setTitle(title);
		result.setHighlightTitle(highlight(highlighter, docId, Constant.NAME,
				title));
		result.setDescription(highlight(highlighter, docId, Constant.BRIEF, doc
				.get(Constant.BRIEF)));
		result.setDetailUrl(doc.get(Constant.URL));
		result.setDownloadUrl(doc.get(Constant.DOWNLOAD_URL));
		result.setCategory(doc.get(Constant.CATEGORY));
		result.setLogo(doc.get(Constant.LOGO));
		result.setVersion(doc.get(Constant.VERSION));
		result.setSize(doc.get(Constant.SIZE));
		result.setStar(doc.get(Constant.STAR));
		result.setPubtime(doc.get(Constant.PUBTIME));
		result.setScore(score);
		return result;
	}

	/**
	 * 创建查询语句
	 * 
	 * @Title: createQuery
	 * @author: jiangzheng
	 * @date: Jun 7, 2011 2:03:55 PM
	 * @param rewriteQuery
	 *            查询重写后生成的Query
	 * @param keyword
	 *            搜索关键词
	 * @param tokens
	 *            分词结果
	 * @param request
	 *            搜索请求对象
	 * @return
	 */
	private static Query createQuery(BooleanQuery rewriteQuery, String keyword,
			SearchRequest request) {
		BooleanQuery query = new BooleanQuery();
		query.add(rewriteQuery, Occur.MUST);
		query.add(createTermQuery(Constant.FULL_NAME, StringUtils
				.transfer(keyword), true), Occur.SHOULD);
		// 查询版本，取指定版本及以前版本的数据
		if (request.getVersion() != 0) {
			NumericRangeQuery<Float> versionRangeQuery = NumericRangeQuery
					.newFloatRange(Constant.VERSION, null,
							request.getVersion(), false, true);
			query.add(versionRangeQuery, Occur.MUST);
		}
		// 取指定大小以内的数据
		if (request.getSize() != 0) {
			NumericRangeQuery<Integer> sizeRangeQuery = NumericRangeQuery
					.newIntRange(Constant.SIZE, null, request.getSize(), false,
							false);
			query.add(sizeRangeQuery, Occur.MUST);
		}
		// 取指定星级以上的数据
		if (request.getStar() != -1) {
			NumericRangeQuery<Float> starRangeQuery = NumericRangeQuery
					.newFloatRange(Constant.STAR, request.getStar(), null,
							true, false);
			query.add(starRangeQuery, Occur.MUST);
		}
		// 取指定时间之后的数据
		if (request.getPubtime() != 0) {
			NumericRangeQuery<Long> pubtimeRangeQuery = NumericRangeQuery
					.newLongRange(Constant.PUBTIME, request.getPubtime(), null,
							true, false);
			query.add(pubtimeRangeQuery, Occur.MUST);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("create query end, query is {}", query);
		}
		return query;
	}

	/**
	 * 将指定字段的值进行高亮
	 * 
	 * @Title: highlight
	 * @author: jiangzheng
	 * @date: Apr 5, 2011 3:57:41 PM
	 * @param fieldName
	 * @param text
	 * @return
	 */
	private static String highlight(Highlighter highlighter, int doc,
			String fieldName, String text) {
		String highlightText = text;
		if (returnFields.containsKey(fieldName)
				&& returnFields.get(fieldName).isHighlight()) {
			highlightText = highlighter.highlight(text);
		}
		return highlightText;
	}

	private static TermQuery createTermQuery(String field, String value,
			boolean useBoost) {
		TermQuery termQuery = new TermQuery(new Term(field, value));
		float boost = 1.0f;
		if (useBoost) {
			if (searchFields.containsKey(field)) {
				boost = searchFields.get(field);
			}
		}
		termQuery.setBoost(boost);
		return termQuery;
	}
}
